﻿using CommunityToolkit.Mvvm.Input;
using HandyControl.Expression.Shapes;
using ImageHandler.Models;
using Microsoft.Win32;
using OpenCvSharp;
using System;
using System.CodeDom.Compiler;
using System.IO;
using System.Windows.Media.Imaging;

namespace ImageHandler.ViewModels
{
    public class E003_UnderlineRecognitionViewModel : PartViewModel
    {
        public E003_UnderlineRecognitionViewModel()
        {
            Header = "下划线识别";
        }

        public override void Run(string path)
        {
            // 读取图像，图像灰度
            Mat src = Cv2.ImRead(path, ImreadModes.Color);
            if (src.Empty())
            {
                OperateLogAction?.Invoke("无法读取图像！");
                return;
            }
            Result.ReslutInfos.Clear();

            Mat mat = new Mat();
            Cv2.CvtColor(src, mat, ColorConversionCodes.BGR2GRAY);
            AddMatToResultInfos(mat, "图像灰度");

            // 获取下划线
            Mat gaussianBlurMat = new Mat();
            Cv2.GaussianBlur(mat, gaussianBlurMat, new Size(3, 3), 15, 0, BorderTypes.Default);
            AddMatToResultInfos(gaussianBlurMat, "高斯模糊");

            Mat laplanceMat = new Mat();
            // Laplacian算子通过计算图像的二阶导数来寻找边缘。
            // 1. src: 输入图像，必须是单通道图像
            // 2. dst: 输出图像，和输入图像大小相同
            // 3. ddepth: 输出图像的深度，通常设为MatType.CV_16S以保留梯度信息，或者MatType.CV_8U来减少图像的动态范围
            // 4. ksize: Sobel算子的大小，通常设为3或5. ksize=3时，使用一个3x3的内核进行卷积。
            // 5. scale: 缩放因子，用于缩放输出图像的值

            //Laplacian对噪声较为敏感，因此需要图像先进行平滑处理（如使用高斯模糊）来减少噪声的影响。
            //相比于Sobel，Laplacian可以检测到更细微的边缘变化，但在检测斜向边缘时效果较差。
            Cv2.Laplacian(gaussianBlurMat, laplanceMat, MatType.CV_8UC1, 3, 3);
            AddMatToResultInfos(laplanceMat, "拉普拉斯");

            Mat binaryImg = new Mat();
            Cv2.Threshold(laplanceMat, binaryImg, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
            AddMatToResultInfos(binaryImg, "二值化");

            Mat morhpImage = new Mat();
            Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(60, 1), new Point(-1, -1));
            Cv2.MorphologyEx(binaryImg, morhpImage, MorphTypes.Open, kernel, new Point(-1, -1));
            AddMatToResultInfos(morhpImage, "形态学操作-开操作");

            kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3), new Point(-1, -1));
            Cv2.Dilate(morhpImage, morhpImage, kernel);
            AddMatToResultInfos(morhpImage, "形态学操作-膨胀操作");

            // 检测直线, 1, PI / 180, 30, 20.0, 0
            // 1: 距离r的精度, 1像素 ,所有可能的长度
            // PI / 180: 角度θ的精度, 1度, 所有可能的角度
            // 30: 检测一条直线所需最少的曲线交点
            // 20.0: 线的最短长度，比这个线短的都会被忽略
            // 0: 两条线之间的最大间隔，如果小于此值，这两条线就会被看成一条线
            LineSegmentPoint[] lines = Cv2.HoughLinesP(morhpImage, 1, Cv2.PI / 180, 30, 20.0, 0);

            Mat retMat = new Mat();
            Cv2.CvtColor(mat, retMat, ColorConversionCodes.GRAY2BGR);
            for (int i = 0; i < lines.Length; i++)
            {
                Cv2.Line(retMat, lines[i].P1, lines[i].P2, new Scalar(0, 0, 255), 2, LineTypes.Link8, 0);
            }
            AddMatToResultInfos(retMat, "识别结果");
            Result.Result = lines.Length + "";
        }
    }
}
